#ifndef __HD44780_ENGINE_H__
#define __HD44780_ENGINE_H__

#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include "hd44780_instructions.h"

#define DISPLAY_BUF_SIZE	1024
#define DISPLAY_PLUS_SIZE	3
#define DISPLAY_BUF_SIZE_PLUS	DISPLAY_BUF_SIZE + DISPLAY_PLUS_SIZE 
#define PREFIX_BUF_SIZE		5
#define SUFFIX_BUF_SIZE		5
#define	DDRAM_CHAR_PER_LINE 	40
#define	DISPLAY_CHAR_PER_LINE 	20

typedef enum {
	FIRST_LINE,
	SECOND_LINE,
	TOTLE_LINES,
} HD44780_DISPLAY_LINE;

typedef enum {
	ALIGN_LEFT,
	CENTERED,
	ALIGN_RIGHT,
} DISPLAY_STYLE;

typedef struct{


	unsigned char	prefix[ PREFIX_BUF_SIZE ];
	unsigned int	prefix_len;

	unsigned char 	display[ DISPLAY_BUF_SIZE_PLUS ];
	unsigned int 	display_len;
	unsigned int	first_pos;

	unsigned char	suffix[ SUFFIX_BUF_SIZE ];
	unsigned int	suffix_len;

	int		cursor_pos;
	bool		blink;
	DISPLAY_STYLE	style;
	
}LINE_CONTENT;

LINE_CONTENT lines[ TOTLE_LINES ];

INLINE void clear_line_content( LINE_CONTENT * this_line_p ){

	memset( this_line_p->prefix  , 0x20 , PREFIX_BUF_SIZE );
	this_line_p->prefix_len 	= 0 ;

	memset( this_line_p->display , 0x20 , DISPLAY_BUF_SIZE_PLUS );
	this_line_p->display_len	= 0 ;
	this_line_p->first_pos 		= 0 ;

	memset( this_line_p->suffix  , 0x20 , SUFFIX_BUF_SIZE );
	this_line_p->suffix_len		= 0 ;

	this_line_p->cursor_pos		= -1;
	this_line_p->blink		= false;
	this_line_p->style		= ALIGN_LEFT;
	return;
}

INLINE void fill_line_content( 	HD44780_DISPLAY_LINE line ,
				unsigned char * prefix_str ,
				unsigned char * display_str , 
				unsigned char * suffix_str , 
				int cursor_pos ,
				bool blink ,
				DISPLAY_STYLE style ){

	LINE_CONTENT * this_line_p = &lines[line];

	clear_line_content( this_line_p );
	
	if( prefix_str ){
		int prefix_len_tmp = strlen( prefix_str );
		int prefix_len = PREFIX_BUF_SIZE > prefix_len_tmp ? prefix_len_tmp : PREFIX_BUF_SIZE;
		this_line_p->prefix_len = prefix_len ;
		memcpy( this_line_p->prefix , prefix_str , prefix_len );
	}
	
	if( suffix_str ){
		int suffix_len_tmp = strlen( suffix_str );
		int suffix_len = SUFFIX_BUF_SIZE > suffix_len_tmp ? suffix_len_tmp : SUFFIX_BUF_SIZE;
		this_line_p->suffix_len = suffix_len ;
		memcpy( this_line_p->suffix , suffix_str , suffix_len );
	}

	if( display_str ){
		int display_len_tmp = strlen( display_str );
		int display_len = DISPLAY_BUF_SIZE > display_len_tmp ?  display_len_tmp: DISPLAY_BUF_SIZE;
		this_line_p->display_len = display_len ;
		memcpy( this_line_p->display , display_str , display_len );

		this_line_p->first_pos = 0;
		
	}


	this_line_p->cursor_pos = cursor_pos ;
	this_line_p->blink	= blink;
	this_line_p->style	= style;
	return;
}

INLINE void HD44780_SET_LINE_TEXT( HD44780_DISPLAY_LINE line , unsigned char string[DISPLAY_CHAR_PER_LINE] ){

	DD_ADDR dr_addr= {0};
	int i;

	switch( line ){
	case ZERO:
		dr_addr.DDA.byte = 0;
		break;
	case ONE:
		dr_addr.DDA.byte = 0x40;
		break;
	default:
		break;
	}

	HD44780_FUNC_set_ddram_addr( dr_addr );

	for( i=0 ; i< DISPLAY_CHAR_PER_LINE ; i++ ){
		HD44780_FUNC_write_data( (OCTER)string[i]);
	}

	return;
}

INLINE void scroll_display( HD44780_DISPLAY_LINE line ){

	unsigned char 	display_char[ DISPLAY_CHAR_PER_LINE ] ;
	LINE_CONTENT  *	this_line_p	= &lines[line];
	unsigned char * prefix_pos	= display_char;
	unsigned char * display_pos	= display_char + this_line_p->prefix_len;
	unsigned char * suffix_pos 	= display_char + DISPLAY_CHAR_PER_LINE - this_line_p->suffix_len ;
	int show_len = DISPLAY_CHAR_PER_LINE - this_line_p->prefix_len - this_line_p->suffix_len;
	int remain_len = show_len - this_line_p->display_len ;

	if( 0 > show_len ){
		fprintf(stderr , "prefix and suffix length overflow the screen !!!\n");
		fflush(stderr);
		return;
	}

	memset( display_char , ' ' , DISPLAY_CHAR_PER_LINE );
	/* filling the screen show with prefix string at the first */
	memcpy( prefix_pos , this_line_p->prefix , this_line_p->prefix_len );
	/* filling the screen show with suffix string at the end */
	memcpy( suffix_pos , this_line_p->suffix , this_line_p->suffix_len );

	int i ;

	/* there is enough space to make the style for displaying */
	if( remain_len >= 0 ){
	
		switch( this_line_p->style ){
		case ALIGN_RIGHT:
			display_pos += remain_len; 
			break;
		case CENTERED:
			display_pos += remain_len/2 ;
			break;
		}
	}

	for( i=0 ; (i < show_len) && (i < this_line_p->display_len) ; i++ ){

		int pos = (i + this_line_p->first_pos ) % (this_line_p->display_len + DISPLAY_PLUS_SIZE );

		*display_pos = this_line_p->display[pos] ;

		display_pos ++ ;
	}

	/* if the buffered chars are more than the number screen can be displayed , then scroll it */
	if( remain_len < 0 ){
		this_line_p->first_pos ++;
		this_line_p->first_pos %= this_line_p->display_len + DISPLAY_PLUS_SIZE ;
	}

	printf("display: %d" , this_line_p->first_pos );
	for(i=0 ; i<DISPLAY_CHAR_PER_LINE ; i++){
		printf("%02x" , display_char[i]);
	}
	printf("\n");

	HD44780_SET_LINE_TEXT( line , display_char );

	if( 0 <= this_line_p->cursor_pos && this_line_p->cursor_pos<= DISPLAY_CHAR_PER_LINE ){

		DD_ADDR dd_addr;

		if( FIRST_LINE == line ){
			dd_addr.DDA.byte = this_line_p->cursor_pos ;
		}else{
			dd_addr.DDA.byte = 0x40+this_line_p->cursor_pos;
		}

		if( this_line_p->blink ){
			HD44780_FUNC_display_on_off_control( 1 , 1 , 1 );
		}else{
			HD44780_FUNC_display_on_off_control( 1 , 1 , 0 );
		}
		
		HD44780_FUNC_set_ddram_addr( dd_addr ) ;
	}
/*	printf("prefix:");
	for(i=0 ; i< this_line_p->prefix_len ; i++){
		printf("%c" , this_line_p->prefix[i]);
	}
	printf("\n");

	printf("display:");
	for(i=0 ; i< this_line_p->display_len ; i++){
		printf("%c" , this_line_p->display[i]);
	}
	printf("\n");

	printf("suffix:");
	for(i=0 ; i< this_line_p->suffix_len ; i++){
		printf("%c" , this_line_p->suffix[i]);
	}
	printf("\n");
*/
	return;
}

/*
INLINE void display_first_line( unsigned char * prefix_str , unsigned char * display_str , unsigned char *suffix_str , int cursor_pos , bool blink ){

	if( display_str ){
		fill_line_content( FIRST_LINE , );
	} 
	scroll_display( FIRST_LINE , prefix_str , display_str , suffix_str , cursor_pos );
	return;
}

INLINE void display_second_line( unsigned char * display_str ){

	return;
}
*/
#endif
